package zdream.pmfield.stage;

import java.util.Arrays;
import java.util.Objects;

import zdream.pmfield.data.foe.MoveCheckPair;
import zdream.pmfield.data.foe.MoveType;
import zdream.pmfield.data.foe.VStat;
import zdream.pmfield.stage.action.AttackAction;
import zdream.pmfield.stage.director.AttackContext;
import zdream.pmfield.stage.director.Director;
import zdream.pmfield.stage.director.MoveChecker;
import zdream.pmfield.stage.message.ActionStartMessage;
import zdream.pmfield.stage.message.AttackMessage;
import zdream.pmfield.stage.message.RollReportMessage;
import zdream.pmfield.stage.object.StageMove;
import zdream.pmfield.test.utils.StageConstant;

import static org.junit.Assert.*;

/**
 * @author Zdream
 * @date 2023-12-14
 * @since 0.0.1
 */
public class AttackChecker {
	public final StageWrapper wrapper;

	public AttackAction lastAction;
	public int[] damageArray = new int[10];
	public float[] ratesArray;
	public int expectedDamage;
	public int maxLevel;

	public AttackChecker(StageWrapper wrapper) {
		this.wrapper = Objects.requireNonNull(wrapper);
	}

	public void init() {
		wrapper.addBasicHandler(ActionStartMessage.class, this::onActionStart);
		wrapper.addBasicHandler(RollReportMessage.class, this::onRollReport);
		wrapper.addBasicHandler(AttackMessage.class, this::onAttack);
	}

	private void onActionStart(ActionStartMessage message) {
		if (!message.action.isForAttack()) {
			lastAction = null;
			return;
		}

		checkAttackAction(message.action.asAttack());
	}

	public void checkAttackAction(AttackAction action) {
		final StageMove move = action.move;
		if (move.attackerUsing != MoveType.PHYSICAL && move.attackerUsing != MoveType.SPECIAL) {
			lastAction = null;
			return;
		}
		if (move.hasTag(StageConstant.TAG_CHARGING)) {
			return;
		}

		final VStat[] vStats = move.vStats;
		if (vStats.length == 0) {
			lastAction = null;
			return;
		}

		MoveChecker moveChecker = wrapper.stage.moveChecker;

		lastAction = action;
		expectedDamage = 0;
		Arrays.fill(damageArray, 0);

		ratesArray = moveChecker.calculateLevelProbability(AttackContext.createForAccuracyChecking(action));
		maxLevel = ratesArray.length;
		for (int i = 0; i <= ratesArray.length; i++) {
			final MoveCheckPair pair = moveChecker.getCheckPairByLevel(move, i);
			AttackContext context = AttackContext.createDryRun(action, pair);
			moveChecker.evaluateAttack(context);
			damageArray[i] = context.mainItem().damage;
		}

		printDamageArray();
	}

	private void onRollReport(RollReportMessage message) {
		if (lastAction == null) {
			return;
		}
		if (!Director.ROLL_TITLE_ACTION.equals(message.title)) {
			return;
		}

		long level = Arrays.stream(message.rolls)
				.filter(roll -> roll.result)
				.count();
		level += Arrays.stream(message.moreRolls)
				.filter(roll -> roll.result)
				.count();

		if (level > maxLevel) {
			lastAction = null;
			return;
		}
		expectedDamage = damageArray[(int) level];
	}

	private void onAttack(AttackMessage message) {
		if (lastAction == null) {
			return;
		}
		if (message.report.items.size() == 0) {
			return;
		}

		assertEquals(expectedDamage, message.report.items.get(0).damage);
	}

	private void printDamageArray() {
		StringBuilder builder = new StringBuilder(100);
		builder.append("[AttackChecker] 预期伤害如下:");
		for (int i = 0; i < maxLevel; i++) {
			builder.append('\n').append("    ").append("当投点为").append(' ').append(i).append(':');
			builder.append(' ').append("伤害为").append(' ').append(damageArray[i]);
			builder.append(' ').append('(').append(ratesArray[i] * 100).append('%').append(')');
		}

		System.out.println(builder.toString());
	}
}
